package com.conf4j.kit;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;

import com.conf4j.Conf;
import com.conf4j.Prop;
import com.conf4j.annotation.Config;
import com.dosgi.kit.StrKit;

/**
 * @author dingnate
 *
 */
public final class ConfKit {
	public static void handler(Class<?> clazz) throws Exception,
			SecurityException {
		Config annotation = clazz.getAnnotation(Config.class);
		if (annotation == null || !Conf.class.isAssignableFrom(clazz))
			return;
		if (StrKit.isBlank(annotation.file()))
			return;

		Prop conf = new Prop(annotation.file());
		Field meField = clazz.getDeclaredField(Conf.SINGLETON_NAME);
		meField.setAccessible(true);
		Object me = meField.get(null);
		if (me == null) {
			me = clazz.newInstance();
			meField.set(null, me);
		}
		Collection<Method> setMethods = getClassSetMethods(clazz);
		for (Method method : setMethods) {
			String value = conf
					.get(getKeyByMethod(annotation.prefix(), method));
			if (StrKit.isNotBlank(value)) {
				Object val = convert(method.getParameterTypes()[0], value);
				method.invoke(me, val);
			}
		}
	}

	private static Collection<Method> getClassSetMethods(Class<?> clazz) {
		Method[] methods = clazz.getMethods();
		Collection<Method> setMethods = new HashSet<Method>();
		for (Method method : methods) {
			if (method.getName().startsWith("set")
					&& method.getName().length() > 3
					&& method.getParameterTypes().length == 1) {
				setMethods.add(method);
			}
		}
		return setMethods;
	}

	private static String getKeyByMethod(String prefix, Method method) {
		String key = StrKit.firstCharToLowerCase(method.getName().substring(3));
		if (StrKit.isNotBlank(prefix)) {
			key = prefix.trim() + "." + key;
		}
		return key;
	}

	private static final Object convert(Class<?> type, String s)
			throws Exception {
		if (type == String.class) {
			return s;
		}

		if (type == Integer.class || type == int.class) {
			return Integer.valueOf(s);
		} else if (type == Long.class || type == long.class) {
			return Long.valueOf(s);
		} else if (type == Double.class || type == double.class) {
			return Double.valueOf(s);
		} else if (type == Float.class || type == float.class) {
			return Float.valueOf(s);
		} else if (type == Boolean.class || type == boolean.class) {
			String value = s.toLowerCase();
			if ("1".equals(value) || "true".equals(value)) {
				return Boolean.TRUE;
			} else if ("0".equals(value) || "false".equals(value)) {
				return Boolean.FALSE;
			} else {
				throw new RuntimeException(
						"Can not parse to boolean type of value: " + s);
			}
		} else if (type == java.math.BigDecimal.class) {
			return new java.math.BigDecimal(s);
		} else if (type == java.math.BigInteger.class) {
			return new java.math.BigInteger(s);
		} else if (type == byte[].class) {
			return s.getBytes();
		}
		throw new Exception(
				type.getName()
						+ " can not be converted, please use other type in your config class!");
	}
}
